{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Intro\n",
    "Exploratory notebook related to linear algebra theory and applications.\n",
    "\n",
    "Examples from [Computational Linear Algebra for Coders](https://github.com/fastai/numerical-linear-algebra/blob/master/README.md)\n",
    "\n",
    "**Linear Algebra**: \"concerning vectors, their properties, mappings and operations\"\n",
    "\n",
    "A **tensor** is the generalization of an array with N axes (dimensions). A 0-Dimensional tensor is a **scalar**, a 1-D tensor is a **vector** and a 2-D tensor is a **matrix**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:17:46.465746",
     "start_time": "2017-09-07T09:17:46.334739"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scalar = np.random.randint(10)\n",
    "#print(scalar.shape) # scalar is an int object, not a numpy array\n",
    "scalar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2,)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([9, 4])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "vector = np.random.randint(10, size=2)\n",
    "print(vector.shape)\n",
    "vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 2)\n"
     ]
    }
   ],
   "source": [
    "matrix = np.random.randint(10, size=(2, 2))\n",
    "print(matrix.shape)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Floating Point Arithmetic\n",
    "Simple demonstration of how floating point arithmetic can differ from the pure mathematical model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-08-29T13:14:17.095888",
     "start_time": "2017-08-29T13:14:17.077887"
    }
   },
   "outputs": [],
   "source": [
    "def f(x):\n",
    "    if x<=1/2:\n",
    "        return x*2\n",
    "    if x>1/2:\n",
    "        return x*2-1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-08-29T13:15:30.096063",
     "start_time": "2017-08-29T13:15:30.075062"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1\n",
      "0.2\n",
      "0.4\n",
      "0.8\n",
      "0.6000000000000001\n",
      "0.20000000000000018\n",
      "0.40000000000000036\n",
      "0.8000000000000007\n",
      "0.6000000000000014\n",
      "0.20000000000000284\n",
      "0.4000000000000057\n",
      "0.8000000000000114\n",
      "0.6000000000000227\n",
      "0.20000000000004547\n",
      "0.40000000000009095\n",
      "0.8000000000001819\n",
      "0.6000000000003638\n",
      "0.2000000000007276\n",
      "0.4000000000014552\n",
      "0.8000000000029104\n",
      "0.6000000000058208\n",
      "0.20000000001164153\n",
      "0.40000000002328306\n",
      "0.8000000000465661\n",
      "0.6000000000931323\n",
      "0.20000000018626451\n",
      "0.40000000037252903\n",
      "0.8000000007450581\n",
      "0.6000000014901161\n",
      "0.20000000298023224\n",
      "0.4000000059604645\n",
      "0.800000011920929\n",
      "0.6000000238418579\n",
      "0.20000004768371582\n",
      "0.40000009536743164\n",
      "0.8000001907348633\n",
      "0.6000003814697266\n",
      "0.20000076293945312\n",
      "0.40000152587890625\n",
      "0.8000030517578125\n",
      "0.600006103515625\n",
      "0.20001220703125\n",
      "0.4000244140625\n",
      "0.800048828125\n",
      "0.60009765625\n",
      "0.2001953125\n",
      "0.400390625\n",
      "0.80078125\n",
      "0.6015625\n",
      "0.203125\n",
      "0.40625\n",
      "0.8125\n",
      "0.625\n",
      "0.25\n",
      "0.5\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n",
      "1.0\n"
     ]
    }
   ],
   "source": [
    "x = 1/10\n",
    "for i in range(80):\n",
    "    print(x)\n",
    "    x = f(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Matrix/Vector Multiplication"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Example from [fastai](http://nbviewer.jupyter.org/github/fastai/numerical-linear-algebra/blob/master/nbs/1.%20Why%20are%20we%20here.ipynb#Matrix-and-Tensor-Products)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T10:09:34.050490",
     "start_time": "2017-09-07T10:09:34.045490"
    }
   },
   "outputs": [],
   "source": [
    "X = np.array([0.85, 0.1, 0.05, 0.]).reshape(1, 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T10:09:37.520689",
     "start_time": "2017-09-07T10:09:37.516688"
    }
   },
   "outputs": [],
   "source": [
    "Y = np.array([[0.9, 0.07, 0.02, 0.01],\n",
    "              [0, 0.93, 0.05, 0.02],\n",
    "              [0, 0, 0.85, 0.15],\n",
    "              [0, 0, 0, 1.]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using multiplication sign for numpy arrays is not equivalent to matrix multiplication, it instead relies on the concept of broadcasting.\n",
    "Proper matrix multiplication can be obtained using `np.dot()` (or `@` in Python 3.x)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T10:09:42.595979",
     "start_time": "2017-09-07T10:09:42.589979"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.765 ,  0.1525,  0.0645,  0.018 ]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.dot(X, Y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T10:10:20.338138",
     "start_time": "2017-09-07T10:10:20.334137"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.765 ,  0.1525,  0.0645,  0.018 ]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X @ Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T10:09:52.142525",
     "start_time": "2017-09-07T10:09:52.137525"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.765 ,  0.007 ,  0.001 ,  0.    ],\n",
       "       [ 0.    ,  0.093 ,  0.0025,  0.    ],\n",
       "       [ 0.    ,  0.    ,  0.0425,  0.    ],\n",
       "       [ 0.    ,  0.    ,  0.    ,  0.    ]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X * Y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Norm\n",
    "Norm is the size of a vector. More generally, p-norm (or $L^p$ norm) of $x$ is defined as \n",
    "\n",
    "$$ \\left\\|\\mathbf {x} \\right\\|_{p}={\\bigg (}\\sum _{i=1}^{n}\\left|x_{i}\\right|^{p}{\\bigg )}^{1/p} $$\n",
    "\n",
    "1-norm = Manhattan norm, simplifies to absolute values sum\n",
    "\n",
    "2-norm = euclidean norm, length or *magnitude* (distance from the origin to the point identified by $x$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:27:29.599100",
     "start_time": "2017-09-07T09:27:29.594099"
    }
   },
   "outputs": [],
   "source": [
    "a = np.array([2,2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:27:30.351143",
     "start_time": "2017-09-07T09:27:30.342142"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# vector 1-norm\n",
    "np.linalg.norm(a, ord=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:27:37.498552",
     "start_time": "2017-09-07T09:27:37.492551"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.8284271247461903"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# vector 2-norm\n",
    "np.linalg.norm(a, ord=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:41:30.986224",
     "start_time": "2017-09-07T09:41:30.981224"
    }
   },
   "outputs": [],
   "source": [
    "m = np.array([[5,3],[0,4]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:41:31.233238",
     "start_time": "2017-09-07T09:41:31.228238"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# matrix 1-norm\n",
    "np.linalg.norm(m, ord=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T09:29:22.655566",
     "start_time": "2017-09-07T09:29:22.628565"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.324555320336759"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# vector 2-norm\n",
    "np.linalg.norm(m, ord=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A *normalized* vector (also known as unit vector) has unit length\n",
    "\n",
    "$$ \\left\\|\\mathbf {x} \\right\\|= 1 $$\n",
    "\n",
    "To normalize a vector simply divide it by its norm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.8284271247461903"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a_len = np.linalg.norm(a, ord=2)\n",
    "a_len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.70710678,  0.70710678])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a / a_len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.99999999999999989"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linalg.norm(a / a_len, ord=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Broadcasting\n",
    "Technique embedded in Numpy (and also other libraries) that allows to operate arithmetic operations between tensors of different shapes, by \"broadcasting\" the smaller array such as to obtain a compatible shape. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:16:32.191268",
     "start_time": "2017-09-07T14:16:32.186268"
    }
   },
   "outputs": [],
   "source": [
    "a = np.array([1,2,3,4])\n",
    "b = np.array([1,5])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:11:50.266143",
     "start_time": "2017-09-07T14:11:50.253143"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-2, -1,  0,  1])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a - 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:13:31.440930",
     "start_time": "2017-09-07T14:13:31.434930"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2],\n",
       "       [-1],\n",
       "       [ 0],\n",
       "       [ 1]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.reshape((4,1)) - 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:13:50.245006",
     "start_time": "2017-09-07T14:13:50.230005"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2, -1],\n",
       "       [ 0,  1]])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.reshape((2,2)) - 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:18:41.612671",
     "start_time": "2017-09-07T14:18:41.604670"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0, -4],\n",
       "       [ 1, -3],\n",
       "       [ 2, -2],\n",
       "       [ 3, -1]])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#a - b #cannot broadcast\n",
    "a.reshape((4,1)) - b.reshape((1, 2)) # both a and b are broadcasted to 4x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-09-07T14:21:16.110508",
     "start_time": "2017-09-07T14:21:16.095507"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0, -3],\n",
       "       [ 2, -1]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.reshape((2, 2)) - b.reshape((1, 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Points and Vectors\n",
    "Geometric interpretation and exploration in 2D Euclidean space.\n",
    "\n",
    "Despite being represented in the same way (e.g. ``p = (1, 2)``) a point and a vector are different geometric entities. A 2D point lives in 2D Euclidean space ($E^2$), a 2D vector lives in 2D linear space ($R^2$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from PIL import Image, ImageDraw\n",
    "\n",
    "#%matplotlib notebook\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "def draw_vector(draw, origin, vector, text=None, fill='black'):\n",
    "    draw.line([tuple(origin), tuple(vector)], fill=fill)\n",
    "    draw.text(tuple(vector), text, fill='black')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# setup PIL image and draw object\n",
    "img_size = 300\n",
    "img = Image.new('RGB', (img_size, img_size), (240, 240, 240))\n",
    "draw = ImageDraw.Draw(img)\n",
    "\n",
    "# draw elements on canvas\n",
    "origin = np.array([0, 0])\n",
    "p = np.array([150, 130])\n",
    "q = np.array([45, 120])\n",
    "\n",
    "# draw p\n",
    "draw.point(p, fill='red')\n",
    "draw_vector(draw, origin, p, 'p')\n",
    "# draw q\n",
    "draw.point(q, fill='red')\n",
    "draw_vector(draw, origin, q, 'q')\n",
    "# draw diff\n",
    "diff_v = p - q\n",
    "draw_vector(draw, origin, diff_v, 'p - q')\n",
    "# draw sum\n",
    "sum_v = p + q\n",
    "draw_vector(draw, origin, sum_v, 'p + q')\n",
    "# draw mean\n",
    "draw_vector(draw, origin, np.mean([p, q], axis=0), 'mean \\n(axis=0)')\n",
    "draw_vector(draw, origin, np.mean([p, q], axis=1), 'mean \\n(axis=1)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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CXEAAIGEOACAQD++vr6uvgYAAFayEgcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAIJMQBAAQS4gAAAglxAACBhDgAgEBCHABAICEOACCQEAcAEEiIAwAI9D/fclZ439fqpwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot\n",
    "fig, ax = plt.subplots(dpi=300, figsize=(2, 2))\n",
    "canvas = ax.imshow(img)\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:anaconda3]",
   "language": "python",
   "name": "conda-env-anaconda3-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  },
  "toc": {
   "nav_menu": {
    "height": "67px",
    "width": "252px"
   },
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": "block",
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
